package org.jmat.domain.entities;

import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
import javax.persistence.EntityResult;
import javax.persistence.FieldResult;

/**
 * This class represents a person.<br />
 * A person is define by :<br />
 * - id : Key<br />
 * - version : use for concurrency <br />
 * - name : the name<br />
 * - firstname : the firstname of the person<br />
 * - birthday : the birthday of the person<br />
 * - address : the address of the person <br />
 * - books : the books of the person<br />
 * - teams : the teams of the person
 * 
 * @author Mathieu Chatelain
 * @version 1.0
 * 
 */

@SuppressWarnings("unused")
@Entity
@Table(name="PERSON")
@NamedQuery(name="getPersonByName",
		query="Select p from Person p where p.name in(:name)")
@SqlResultSetMapping(name="PersonWithBooksResults",
				entities={
					@EntityResult(entityClass=Person.class),
					@EntityResult(entityClass=Book.class, fields={
							@FieldResult(name="id", column="book_id")
					})
		})
public class Person implements Serializable {

	private static final long serialVersionUID = -8974430355120821202L;
	private Integer id;
	private int version;
	private String name;
	private String firstname;
	private Date birthday;
	private Address address;
	private Coach coach;          
	private Set<Book> books = new HashSet<Book>();
	private Set<Team> teams = new HashSet<Team>();
	private Set<Animal> animals = new HashSet<Animal>();
	
	// constructor
	public Person() {
	}

	public Person(String name, String firstname, Date birthday) {
		super();
		this.name = name;
		this.firstname = firstname;
		this.birthday = birthday;
	}
	
	public Person(String name, String firstname, Date birthday, Address address) {
		super();
		this.name = name;
		this.firstname = firstname;
		this.birthday = birthday;
		this.address = address;
	}

	/**
	 * 
	 * @return Integer - id of the person
	 */
	@Id
	@Column(name = "ID", nullable = false)
	@GeneratedValue(strategy = GenerationType.AUTO)
	public Integer getId() {
		return id;
	}

	/**
	 * 
	 * @param id - Generated auto.
	 */
	private void setId(Integer id) {
		this.id = id;
	}

	/**
	 * 
	 * @return int - Version of the object Person
	 */
	@Column(name = "VERSION", nullable = false)
	@Version
	public int getVersion() {
		return version;
	}

	/**
	 * 
	 * @param version - Generated by JPA
	 */
	private void setVersion(int version) {
		this.version = version;
	}

	/**
	 * 
	 * @return String - get name of a person
	 */
	@Column(name = "NAME", length = 30, nullable = false)
	public String getName() {
		return name;
	}

	/**
	 * 
	 * @param String - set name of a person
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * 
	 * @return String - get firstname of a person
	 */
	@Column(name = "FIRSTNAME", length = 30, nullable = false)
	public String getFirstname() {
		return firstname;
	}

	/**
	 * 
	 * @param String - set firstname of a person
	 */
	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	/**
	 * 
	 * @return Date - get birthday of a person
	 */
	@Column(name = "BIRTHDAY", nullable = false)
	@Temporal(TemporalType.DATE)
	public Date getBirthday() {
		return birthday;
	}

	/**
	 * 
	 * @param Date - set birthday of a person
	 */
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	
	/**
	 * 
	 * @return Address - get address of a person
	 */
	@Embedded
	public Address getAddress() {
		return address;
	}

	/**
	 * 
	 * @param address - set address of a person
	 */
	public void setAddress(Address address) {
		this.address = address;
	}
	
	/**
	 * 
	 * @return
	 */
	@OneToOne(cascade = { CascadeType.ALL })
	public Coach getCoach() {
		return coach;
	}

	/**
	 * 
	 * @param coach
	 */
	public void setCoach(Coach coach) {
		this.coach = coach;
	}

	public void addCoach(Coach coach){
		this.coach = coach;
		coach.setPerson(this);
	}
	
	/**
	 * 
	 * @return Set<Book> - get the books of a person
	 */
	@OneToMany(mappedBy = "person", cascade = { CascadeType.ALL })
	public Set<Book> getBooks() {
		return books;
	}

	/**
	 * 
	 * @param books - set books of a person
	 */
	public void setBooks(Set<Book> books) {
		this.books = books;
	}
	
	/**
	 * 
	 * @param book - add a book to a person. Asociation between person and book
	 */
	public void addBook(Book book){
		books.add(book);
		book.setPerson(this);
	}

	/**
	 * 
	 * @return Set<Team> - get the teams of a person
	 */
	@ManyToMany(cascade = { CascadeType.PERSIST }, fetch = FetchType.LAZY)
	@JoinTable(name = "PERSON_TEAM", joinColumns = @JoinColumn(name = "PERSON_ID"), inverseJoinColumns = @JoinColumn(name = "TEAM_ID"))
	public Set<Team> getTeams() {
		return teams;
	}

	/**
	 * 
	 * @param teams - the teams of a person
	 */
	public void setTeams(Set<Team> teams) {
		this.teams = teams;
	}

	/**
	 * 
	 * @return Animals of the person
	 */
	@OneToMany(mappedBy = "prop", cascade = { CascadeType.ALL })
	public Set<Animal> getAnimals() {
		return animals;
	}

	/**
	 * 
	 * @param animals
	 */
	public void setAnimals(Set<Animal> animals) {
		this.animals = animals;
	}
	
	public void addAnimal(Animal animal){
		animals.add(animal);
		animal.setProp(this);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public String toString(){
		String str = " _____________________________________________\n";
		str = str + "|                PERSON                       |\n";
		str = str + "|_____________________________________________|\n";
		str = str + "| Id : " + this.getId() + "\n";
		str = str + "| Name : " + this.getName() + "\n";
		str = str + "| Firstname : " + this.getFirstname() + "\n";
		str = str + "| Birthday : " + this.getBirthday() + "\n";
		if (this.getAddress() != null){
			str = str + "| Address : " + this.getAddress().getNumber() + " " + this.getAddress().getStreet() + "\n| " + this.getAddress().getCity() + "\n";
		}
		if(this.coach != null){
			str = str + "| __________________COACH___________________\n";
			str = str + "| " + this.coach.getName();
		}
		if (this.getBooks() != null){
			str = str + "| __________________BOOKS___________________\n";
			for (Book b : this.getBooks()){
				str = str + "   - Id : " + b.getId() + "\n";
				str = str + "   - Author : " + b.getAuthor() + "\n";
				str = str + "   - Title : " + b.getTitle() + "\n";
				str = str + "   ----------\n";
			}
		}
		if (this.getAnimals() != null){
			str = str + "| __________________ANIMALS_________________\n";
			for (Animal a : this.getAnimals()){
				if (a instanceof Dog){
					str = str + "|   -- DOGS --   \n";
					Dog aDog = new Dog();
					aDog = (Dog)a;
					str = str + "   - Id : " + aDog.getId() + "\n";
					str = str + "   - Name : " + aDog.getName() + "\n";
					str = str + "   - Race : " + aDog.getRace() + "\n";
				}
				else if (a instanceof Cat){
					str = str + "|   -- CATS --   \n";
					Cat aCat = new Cat();
					aCat = (Cat)a;
					str = str + "   - Id : " + aCat.getId() + "\n";
					str = str + "   - Name : " + aCat.getName() + "\n";
					str = str + "   - Race : " + aCat.getRace() + "\n";
				}
			}	
		}
		if (this.getTeams() != null){
			str = str + "| __________________TEAMS___________________\n";
			for (Team t : this.getTeams()){
				str = str + "   - Id : " + t.getId() + "\n";
				str = str + "   - Name : " + t.getName() + "\n";
				str = str + "   ----------\n";
			}
		}
		return str;
	}
	
	

}
